conf = require("../config")
if not conf.nvim_cmp.enabled then return {} end

return {
  "hrsh7th/nvim-cmp",
  event = {
    "InsertEnter",
    "CmdlineEnter",
  },
  dependencies = {
    "hrsh7th/cmp-nvim-lsp",
    "hrsh7th/cmp-buffer",
    "hrsh7th/cmp-path",
    "hrsh7th/cmp-cmdline",
    "onsails/lspkind.nvim",
    "L3MON4D3/LuaSnip",
    "folke/lazydev.nvim",
    "saadparwaiz1/cmp_luasnip",
  },
  config = function ()
    local has_words_before = function()
        unpack = unpack or table.unpack
        local line, col = unpack(vim.api.nvim_win_get_cursor(0))
        return col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match("%s") == nil
    end

    local luasnip = require("luasnip")
    local lspkind = require("lspkind")
    local cmp = require("cmp")

    cmp.setup({
      snippet = {
        -- REQUIRED - you must specify a snippet engine
        expand = function(args)
          require('luasnip').lsp_expand(args.body) -- For `luasnip` users.
        end,
      },
      mapping = cmp.mapping.preset.insert({
        ['<C-c>'] = cmp.mapping.complete(),
        -- Use <C-b/f> to scroll the docs
        ['<C-b>'] = cmp.mapping.scroll_docs( -4),
        ['<C-f>'] = cmp.mapping.scroll_docs(4),
        -- Use <C-m/n> to switch in items
        ['<C-k>'] = cmp.mapping.select_prev_item(),
        ['<C-j>'] = cmp.mapping.select_next_item(),
        -- Use <CR>(Enter) to confirm selection
        -- Accept currently selected item. Set `select` to `false` to only confirm explicitly selected items.
        ['<CR>'] = cmp.mapping.confirm({ select = true }),

        -- A super tab
        -- sourc: https://github.com/hrsh7th/nvim-cmp/wiki/Example-mappings#luasnip
        ["<Tab>"] = cmp.mapping(function(fallback)
          -- Hint: if the completion menu is visible select next one
          -- if cmp.visible() then
          --   cmp.select_next_item()
          -- elseif luasnip.locally_jumpable(1) then
          --   luasnip.jump(1)
          -- else
          --   fallback()
          -- end

          if luasnip.locally_jumpable(1) then
            luasnip.jump(1)
          else
            fallback()
          end

        end, { "i", "s" }), -- i - insert mode; s - select mode
        ["<S-Tab>"] = cmp.mapping(function(fallback)
          -- if cmp.visible() then
          --   cmp.select_prev_item()
          -- elseif luasnip.locally_jumpable(-1) then
          --   luasnip.jump(-1)
          -- else
          --   fallback()
          -- end

          if luasnip.locally_jumpable(-1) then
            luasnip.jump(-1)
          else
            fallback()
          end

        end, { "i", "s" }),
      }),

      -- Let's configure the item's appearance
      -- source: https://github.com/hrsh7th/nvim-cmp/wiki/Menu-Appearance
      formatting = {
        -- Set order from left to right
        -- kind: single letter indicating the type of completion
        -- abbr: abbreviation of "word"; when not empty it is used in the menu instead of "word"
        -- menu: extra text for the popup menu, displayed after "word" or "abbr"
        fields = { 'abbr', 'kind', 'menu' },

        -- customize the appearance of the completion menu
        format = function(entry, vim_item)
          if vim.tbl_contains({ 'path' }, entry.source.name) then
            local icon, hl_group = require('nvim-web-devicons').get_icon(entry:get_completion_item().label)
            if icon then
              vim_item.kind = icon
              vim_item.kind_hl_group = hl_group
              return vim_item
            end
          end
          return lspkind.cmp_format({
            mode = 'symbol_text',
            maxwidth = 50,
            ellipsis_char = '...',
            show_labelDetails = true,

            before = function (entry, vim_item)
              vim_item.menu = ({
                nvim_lsp = '[Lsp]',
                luasnip = '[Luasnip]',
                buffer = '[File]',
                path = '[Path]',
              })[entry.source.name]
              return vim_item
            end
          })(entry, vim_item)
        end,
      },

      window = {
        completion = cmp.config.window.bordered(),
        documentation = cmp.config.window.bordered(),
      },

      experimental = {
        ghost_text = true,
      },

      -- Set source precedence
      sources = cmp.config.sources({
        { name = 'nvim_lsp' },    -- For nvim-lsp
        { name = 'luasnip' },     -- For luasnip user
        { name = 'buffer' },      -- For buffer word completion
        { name = 'path' },        -- For path completion
        {
          name = 'lazydev',
          group_index = 0
        },
      })
    })

    cmp.setup.cmdline({ '/', '?' }, {
      mapping = cmp.mapping.preset.cmdline(),
      sources = {
        { name = 'buffer' }
      }
    })

    cmp.setup.cmdline(':', {
      mapping = cmp.mapping.preset.cmdline(),
      sources = cmp.config.sources({
        { name = 'path' }
      }, {
        { name = 'cmdline' }
      }),
      matching = { disallow_symbol_nonprefix_matching = false }
    })
  end,
}
